#ifndef MATH3D_H
#define MATH3D_H

#include "vertex3D.h"
#include <QOpenGLWidget>

#include <QObject>
#include <QList>

/** 王诗阳几何体对象 */
class Math3D : public QObject
{
    Q_OBJECT
public:
    Math3D(){}
    /** 新的实例 */
    static GLfloat *newInstance() {
        return new GLfloat[3];
    }
    /** 设置新的值 */
    static void setNewValue(float a, float b, float c, GLfloat *value) {
        value[0] = a; value[1] = b; value[2] = c;
    }
    /***/
    static void setNewValueWithVector(GLfloat *_old, GLfloat *_new) {
        setNewValue(_old[0], _old[1], _old[2], _new);
    }
    static GLfloat *newInstanceWithVertex3D(Vertex3D *v) {
        GLfloat *returnValue = newInstance();
        setNewValue(v->getX(), v->getY(), v->getZ(), returnValue);
        return returnValue;
    }
    static GLfloat *newInstanceWithValue(float a, float b, float c) {
        GLfloat *returnValue = newInstance();
        setNewValue(a, b, c, returnValue);
        return returnValue;
    }
    static GLfloat *newInstanceWithOldVector(GLfloat *_old) {
        GLfloat *_new = newInstance();
        setNewValueWithVector(_old, _new);
        return _new;
    }
    /** 将向量进行相乘 */
    static void multiplyVector(double value, GLfloat *array) {
        array[0] = array[0] * value;
        array[1] = array[1] * value;
        array[2] = array[2] * value;
    }
    /** 将向量进行相乘，并赋值给新的数组中 */
    static void multiplyVectorNew(double value, GLfloat *old, GLfloat *_new) {
        _new[0] = old[0] * value;
        _new[1] = old[1] * value;
        _new[2] = old[2] * value;
    }
    /**
     * @brief 根据两个点建立向量
     * @param pointA
     * @param pointB
     * @return
     */
    static Vector3D *direction(Vertex3D *pointA, Vertex3D *pointB) {
        double a_x = pointA->getX(),
                a_y = pointA->getY(),
                a_z = pointA->getZ();

        double b_x = pointB->getX(),
                b_y = pointB->getY(),
                b_z = pointB->getZ();

        return new Vector3D(b_x - a_x, b_y - a_y, b_z - a_z);
    }

    static void normalize(GLfloat *array) {
        double deltaX = array[0], deltaY = array[1], deltaZ = array[2];
        double length = sqrt(pow(deltaX, 2) + pow(deltaY, 2) + pow(deltaZ, 2));
        array[0] = deltaX / length;
        array[1] = deltaY / length;
        array[2] = deltaZ / length;
    }


    static void directionNew(Vertex3D *pointA, Vertex3D *pointB, GLfloat *array) {
        double a_x = pointA->getX(),
                a_y = pointA->getY(),
                a_z = pointA->getZ();

        double b_x = pointB->getX(),
                b_y = pointB->getY(),
                b_z = pointB->getZ();

        float deltaX = b_x - a_x, deltaY = b_y - a_y, deltaZ = b_z - a_z;
        array[0] = deltaX;
        array[1] = deltaY;
        array[2] = deltaZ;
    }

    static void directionNew2(GLfloat *pointA, GLfloat *pointB, GLfloat *array) {
        float a_x = pointA[0], a_y = pointA[1], a_z = pointA[2];

        float b_x = pointB[0], b_y = pointB[1], b_z = pointB[2];

        float deltaX = b_x - a_x, deltaY = b_y - a_y, deltaZ = b_z - a_z;
        array[0] = deltaX;
        array[1] = deltaY;
        array[2] = deltaZ;
    }

    static float lengthOfVector(GLfloat *ver) {
        float deltaX = ver[0], deltaY = ver[1], deltaZ = ver[2];
        return sqrt(pow(deltaX, 2) + pow(deltaY, 2) + pow(deltaZ, 2));
    }

    static void normalizeWithChanging(GLfloat *array) {
        normalizeWithoutChanging(array, array);
    }

    static void normalizeWithoutChanging(GLfloat *array, GLfloat *returnValue) {
        float deltaX = array[0], deltaY = array[1], deltaZ = array[2];
        float length = sqrt(pow(deltaX, 2) + pow(deltaY, 2) + pow(deltaZ, 2));
        returnValue[0] = deltaX / length;
        returnValue[1] = deltaY / length;
        returnValue[2] = deltaZ / length;

    }

    static void directionNewAndNormalize(Vertex3D *pointA, Vertex3D *pointB, GLfloat *array) {
        directionNew(pointA, pointB, array);
        normalizeWithChanging(array);
    }

    /** 计算两个向量的叉乘，其几何意义是计算两个向量的垂直方向 */
    static Vector3D *crossVectors(Vector3D *d1, Vector3D *d2) {
        double ax = d1->getDeltaX(), ay = d1->getDeltaY(), az = d1->getDeltaZ();
        double bx = d2->getDeltaX(), by = d2->getDeltaY(), bz = d2->getDeltaZ();

        double dx = ay * bz - az * by;
        double dy = az * bx - ax * bz;
        double dz = ax * by - ay * bx;

        return new Vector3D(dx, dy, dz);
    }
    static void crossVectorsNew(GLfloat *d1, GLfloat *d2, GLfloat *array) {
        float ax = d1[0], ay = d1[1], az = d1[2];
        float bx = d2[0], by = d2[1], bz = d2[2];

        array[0] = ay * bz - az * by;
        array[1] = az * bx - ax * bz;
        array[2] = ax * by - ay * bx;
    }
    /** 计算出两个向量的乘积，通常用于判断两个向量是否垂直（新版） */
    static float dotNew(GLfloat *d1, GLfloat *d2) {
        float ax = d1[0], ay = d1[1], az = d1[2];
        float bx = d2[0], by = d2[1], bz = d2[2];

        return ax * bx + ay * by + az * bz;
    }
    /** 计算出两个向量的乘积，通常用于判断两个向量是否垂直 */
    static double dot(Vector3D *d1, Vector3D *d2) {
        double ax = d1->getDeltaX(), ay = d1->getDeltaY(), az = d1->getDeltaZ();
        double bx = d2->getDeltaX(), by = d2->getDeltaY(), bz = d2->getDeltaZ();

        double value =  ax * bx + ay * by + az * bz;
        return value;
    }
    /** 多个向量相加（第二版） */
    static void addSingleVectorNew(GLfloat *vectorToAdd, GLfloat *vectorAdded) {
        setNewValue(
                vectorAdded[0] + vectorToAdd[0],
                vectorAdded[1] + vectorToAdd[1],
                vectorAdded[2] + vectorToAdd[2], vectorAdded);
    }
    /** 多个向量相加（第三版） */
    static void addVectorsNew(GLfloat *vector1, GLfloat *vector2, GLfloat *targetVector) {
        targetVector[0] = vector1[0] + vector2[0];
        targetVector[1] = vector1[1] + vector2[1];
        targetVector[2] = vector1[2] + vector2[2];
    }
    /** 多个向量相加 */
    static Vector3D *addVectors(QList<Vector3D*> *vectors) {
        int count = vectors->length();
        double deltaX = 0, deltaY = 0, deltaZ = 0;
        for (int i = 0; i < count; i++) {
            Vector3D *vector = vectors->at(i);
            deltaX += vector->getDeltaX();
            deltaY += vector->getDeltaY();
            deltaZ += vector->getDeltaZ();
        }

        return new Vector3D(deltaX, deltaY, deltaZ);
    }
    /** 计算两个向量的夹角（以弧度表示） */
    static double angleOfTwoVectorsInRad(Vector3D *d1, Vector3D *d2) {
        double x1 = d1->getDeltaX(), x2 = d2->getDeltaX();
        double y1 = d1->getDeltaY(), y2 = d2->getDeltaY();
        double z1 = d1->getDeltaZ(), z2 = d2->getDeltaZ();
        double n = x1 * x2 + y1 * y2 + z1 * z2;
        double m = d1->length() * d2->length();

        return acos(n/m);
    }
    /** 以法向量为参考向量，计算出一个向量的反射路径（第二版） */
    static void computeReflectionVectorNew(GLfloat *a, GLfloat *n, GLfloat *returnValue) {
        double aLength = lengthOfVector(a), nLength = lengthOfVector(n);
        double cosTheta = dotNew(a, n) / (aLength * nLength);
        normalizeWithChanging(n);
        multiplyVector(2 * aLength, n);
        returnValue[0] = n[0] - a[0];
        returnValue[1] = n[1] - a[1];
        returnValue[2] = n[2] - a[2];
    }

    /** 以法向量为参考向量，计算出一个向量的反射路径 */
    static Vector3D *computeReflectionVector(Vector3D *a, Vector3D *n) {
        double cosTheta = dot(a, n) / (a->length() * n->length());//cosθ
        Vector3D *accumulatedVector = n->normalize()->multiply(2 * a->length() * cosTheta);//na = n * (2 * |a| * cosθ)
        return accumulatedVector->addNewVector(a->multiply(-1));//na - a
    }

    /** 获取两个点的中点 */
    static Vertex3D *middleVertex(Vertex3D *a, Vertex3D *b) {
        double x = (a->getX() + b->getX()) / 2,
               y = (a->getY() + b->getY()) / 2,
               z = (a->getZ() + b->getZ()) / 2;

        return new Vertex3D(x, y, z);
    }

    static GLfloat *ComputeJoinedVertexOfLineAndPlane(QObject *_line, QObject *_plane);

//    /** 按分段获取一个圆弧的路径 */
//    static QList<Vertex3D *> *VerticesAlongTheArc(Vertex3D *fromPoint, Vertex3D *toPoint,
//                                                  Vertex3D *center, double radius,
//                                                  int segment);
};

#endif // MATH3D_H
